home *** CD-ROM | disk | FTP | other *** search
/ MacWorld 2003 August / MW 8 2003 CD1.iso / Inside Macworld / Product News / gimp-1.2.4.sit / gimp-1.2.4 / plug-ins / common / smooth_palette.c < prev    next >
Encoding:
C/C++ Source or Header  |  2002-02-03  |  11.3 KB  |  466 lines

  1. /*
  2.    smooth palette - derive smooth palette from image
  3.    Copyright (C) 1997  Scott Draves <spot@cs.cmu.edu>
  4.  
  5.    The GIMP -- an image manipulation program
  6.    Copyright (C) 1995 Spencer Kimball and Peter Mattis
  7.  
  8.    This program is free software; you can redistribute it and/or modify
  9.    it under the terms of the GNU General Public License as published by
  10.    the Free Software Foundation; either version 2 of the License, or
  11.    (at your option) any later version.
  12.  
  13.    This program is distributed in the hope that it will be useful,
  14.    but WITHOUT ANY WARRANTY; without even the implied warranty of
  15.    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  16.    GNU General Public License for more details.
  17.  
  18.    You should have received a copy of the GNU General Public License
  19.    along with this program; if not, write to the Free Software
  20.    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
  21. */
  22.  
  23. #include "config.h"
  24.  
  25. #include <stdlib.h>
  26. #include <stdio.h>
  27. #include <string.h>
  28. #include <time.h>
  29.  
  30. #include <gtk/gtk.h>
  31.  
  32. #include <libgimp/gimp.h>
  33. #include <libgimp/gimpui.h>
  34.  
  35. #include "libgimp/stdplugins-intl.h"
  36.  
  37.  
  38. /* Declare local functions. */
  39. static void query  (void);
  40. static void run    (gchar   *name,
  41.             gint     nparams,
  42.             GimpParam  *param,
  43.             gint    *nreturn_vals,
  44.             GimpParam **return_vals);
  45.  
  46. static gint   dialog (void);
  47.  
  48. static gint32 doit   (GimpDrawable *drawable,
  49.               gint32    *layer_id);
  50.  
  51. GimpPlugInInfo PLUG_IN_INFO =
  52. {
  53.   NULL,  /* init_proc  */
  54.   NULL,  /* quit_proc  */
  55.   query, /* query_proc */
  56.   run,   /* run_proc   */
  57. };
  58.  
  59. gboolean run_flag = FALSE;
  60.  
  61. MAIN ()
  62.  
  63. static void
  64. query (void)
  65. {
  66.   static GimpParamDef args[] =
  67.   {
  68.     { GIMP_PDB_INT32, "run_mode", "Interactive, non-interactive" },
  69.     { GIMP_PDB_IMAGE, "image", "Input image (unused)" },
  70.     { GIMP_PDB_DRAWABLE, "drawable", "Input drawable" },
  71.     { GIMP_PDB_INT32, "width", "Width" },
  72.     { GIMP_PDB_INT32, "height", "Height" },
  73.     { GIMP_PDB_INT32, "ntries", "Search Time" },
  74.     { GIMP_PDB_INT32, "show_image","Show Image?" }
  75.   };
  76.   static gint nargs = sizeof (args) / sizeof (args[0]);
  77.  
  78.   static GimpParamDef return_vals[] =
  79.   {
  80.     { GIMP_PDB_IMAGE, "new_image", "Output image" },
  81.     { GIMP_PDB_LAYER, "new_layer", "Output layer" }
  82.   };
  83.   static gint nreturn_vals = sizeof (return_vals) / sizeof (return_vals[0]);
  84.  
  85.   gimp_install_procedure ("plug_in_smooth_palette",
  86.               "derive smooth palette from image",
  87.               "help!",
  88.               "Scott Draves",
  89.               "Scott Draves",
  90.               "1997",
  91.               N_("<Image>/Filters/Colors/Smooth Palette..."),
  92.               "RGB*",
  93.               GIMP_PLUGIN,
  94.               nargs, nreturn_vals,
  95.               args, return_vals);
  96. }
  97.  
  98. static struct
  99. {
  100.   gint width;
  101.   gint height;
  102.   gint ntries;
  103.   gint try_size;
  104.   gint show_image;
  105. } config =
  106. {
  107.   256,
  108.   64,
  109.   50,
  110.   10000,
  111.   1
  112. };
  113.  
  114. static void
  115. run (gchar   *name,
  116.      gint     nparams,
  117.      GimpParam  *param,
  118.      gint    *nreturn_vals,
  119.      GimpParam **return_vals)
  120. {
  121.   static GimpParam values[3];
  122.   GimpRunModeType run_mode;
  123.   GimpPDBStatusType status = GIMP_PDB_SUCCESS;
  124.   GimpDrawable *drawable;
  125.  
  126.   run_mode = param[0].data.d_int32;
  127.  
  128.   *nreturn_vals = 3;
  129.   *return_vals = values;
  130.  
  131.   values[0].type = GIMP_PDB_STATUS;
  132.   values[0].data.d_status = status;
  133.   values[1].type = GIMP_PDB_IMAGE;
  134.   values[2].type = GIMP_PDB_LAYER;
  135.  
  136.   switch (run_mode)
  137.     {
  138.     case GIMP_RUN_INTERACTIVE:
  139.       INIT_I18N_UI();
  140.       gimp_get_data ("plug_in_smooth_palette", &config);
  141.       if (! dialog ())
  142.     return;
  143.       break;
  144.  
  145.     case GIMP_RUN_NONINTERACTIVE:
  146.       INIT_I18N();
  147.       if (nparams != 7)
  148.     {
  149.       status = GIMP_PDB_CALLING_ERROR;
  150.     }
  151.       else
  152.     {
  153.       config.width      = param[3].data.d_int32;
  154.       config.height     = param[4].data.d_int32;
  155.       config.ntries     = param[5].data.d_int32;
  156.       config.show_image = param[6].data.d_int32 ? TRUE : FALSE;
  157.     }
  158.  
  159.       if (status == GIMP_PDB_SUCCESS && 
  160.       ((config.width <= 0) || (config.height <= 0) || config.ntries <= 0))
  161.     status = GIMP_PDB_CALLING_ERROR;
  162.  
  163.       break;
  164.  
  165.     case GIMP_RUN_WITH_LAST_VALS:
  166.       INIT_I18N();
  167.       /*  Possibly retrieve data  */
  168.       gimp_get_data ("plug_in_smooth_palette", &config);
  169.       break;
  170.  
  171.     default:
  172.       break;
  173.     }
  174.  
  175.   if (status == GIMP_PDB_SUCCESS)
  176.     {
  177.       drawable = gimp_drawable_get (param[2].data.d_drawable);
  178.       if (gimp_drawable_is_rgb (drawable->id))
  179.     {
  180.       gimp_progress_init (_("Deriving smooth palette..."));
  181.       gimp_tile_cache_ntiles (2 * (drawable->width + 1) /
  182.                   gimp_tile_width ());
  183.       values[1].data.d_image = doit (drawable, &values[2].data.d_layer);
  184.       if (run_mode == GIMP_RUN_INTERACTIVE)
  185.         gimp_set_data ("plug_in_smooth_palette", &config, sizeof (config));
  186.       if (config.show_image)
  187.         gimp_display_new (values[1].data.d_image);
  188.     }
  189.       else
  190.     status = GIMP_PDB_EXECUTION_ERROR;
  191.       gimp_drawable_detach (drawable);
  192.   }
  193.  
  194.   values[0].data.d_status = status;
  195. }
  196.  
  197. static long
  198. pix_diff (guchar *pal,
  199.       gint    bpp,
  200.       gint    i,
  201.       gint    j)
  202. {
  203.   glong r = 0;
  204.   gint k;
  205.  
  206.   for (k = 0; k < bpp; k++)
  207.     {
  208.       int p1 = pal[j * bpp + k];
  209.       int p2 = pal[i * bpp + k];
  210.       r += (p1 - p2) * (p1 - p2);
  211.     }
  212.  
  213.   return r;
  214. }
  215.  
  216. static void
  217. pix_swap (guchar *pal,
  218.       gint    bpp,
  219.       gint    i,
  220.       gint    j)
  221. {
  222.   gint k;
  223.  
  224.   for (k = 0; k < bpp; k++)
  225.     {
  226.       guchar t = pal[j * bpp + k];
  227.       pal[j * bpp + k] = pal[i * bpp + k];
  228.       pal[i * bpp + k] = t;
  229.     }
  230. }
  231.  
  232. static gint32
  233. doit (GimpDrawable *drawable,
  234.       gint32    *layer_id)
  235. {
  236.   gint32     new_image_id;
  237.   GimpDrawable *new_layer;
  238.   gint       psize, i, j;
  239.   guchar    *pal;
  240.   gint       bpp = drawable->bpp;
  241.   GimpPixelRgn  pr;
  242.  
  243.   srand(time(0));
  244.  
  245.   new_image_id = gimp_image_new (config.width, config.height, GIMP_RGB);
  246.   *layer_id = gimp_layer_new (new_image_id, _("Background"),
  247.                   config.width, config.height,
  248.                   gimp_drawable_type (drawable->id),
  249.                   100, GIMP_NORMAL_MODE);
  250.   gimp_image_add_layer (new_image_id, *layer_id, 0);
  251.   new_layer = gimp_drawable_get (*layer_id);
  252.  
  253.   psize = config.width;
  254.  
  255.   pal = g_malloc (psize * bpp);
  256.  
  257.   gimp_pixel_rgn_init (&pr, drawable, 0, 0, drawable->width,
  258.                drawable->height,
  259.                FALSE, FALSE);
  260.  
  261.   /* get initial palette */
  262.   for (i = 0; i < psize; i++)
  263.     {
  264.       gint x = rand() % drawable->width;
  265.       gint y = rand() % drawable->height;
  266.  
  267.       gimp_pixel_rgn_get_pixel (&pr, pal + bpp * i, x, y);
  268.     }
  269.  
  270.   /* reorder */
  271.   if (1)
  272.     {
  273.       guchar  *pal_best = g_malloc (psize * bpp);
  274.       guchar  *original = g_malloc (psize * bpp);
  275.       gdouble  len_best = 0;
  276.       gint     try;
  277.  
  278.       memcpy (pal_best, pal, bpp * psize);
  279.       memcpy (original, pal, bpp * psize);
  280.  
  281.       for (try = 0; try < config.ntries; try++)
  282.     {
  283.       gdouble len;
  284.  
  285.       if (!(try%5))
  286.         gimp_progress_update (try / (double) config.ntries);
  287.       memcpy (pal, original, bpp * psize);
  288.  
  289.       /* scramble */
  290.       for (i = 1; i < psize; i++)
  291.         pix_swap (pal, bpp, i, rand() % psize);
  292.  
  293.       /* measure */
  294.       len = 0.0;
  295.       for (i = 1; i < psize; i++)
  296.         len += pix_diff (pal, bpp, i, i-1);
  297.  
  298.       /* improve */
  299.       for (i = 0; i < config.try_size; i++)
  300.         {
  301.           gint  i0 = 1 + (rand() % (psize-2));
  302.           gint  i1 = 1 + (rand() % (psize-2));
  303.           glong as_is, swapd;
  304.  
  305.           if (1 == (i0 - i1))
  306.         {
  307.           as_is = (pix_diff (pal, bpp, i1 - 1, i1) +
  308.                pix_diff (pal, bpp, i0, i0 + 1));
  309.           swapd = (pix_diff (pal, bpp, i1 - 1, i0) +
  310.                pix_diff (pal, bpp, i1, i0 + 1));
  311.         }
  312.           else if (1 == (i1 - i0))
  313.         {
  314.           as_is = (pix_diff (pal, bpp, i0 - 1, i0) +
  315.                pix_diff (pal, bpp, i1, i1 + 1));
  316.           swapd = (pix_diff (pal, bpp, i0 - 1, i1) +
  317.                pix_diff (pal, bpp, i0, i1 + 1));
  318.         }
  319.           else
  320.         {
  321.           as_is = (pix_diff (pal, bpp, i0, i0 + 1) +
  322.                pix_diff (pal, bpp, i0, i0 - 1) +
  323.                pix_diff (pal, bpp, i1, i1 + 1) +
  324.                pix_diff (pal, bpp, i1, i1 - 1));
  325.           swapd = (pix_diff (pal, bpp, i1, i0 + 1) +
  326.                pix_diff (pal, bpp, i1, i0 - 1) +
  327.                pix_diff (pal, bpp, i0, i1 + 1) +
  328.                pix_diff (pal, bpp, i0, i1 - 1));
  329.         }
  330.           if (swapd < as_is)
  331.         {
  332.           pix_swap (pal, bpp, i0, i1);
  333.           len += swapd - as_is;
  334.         }
  335.         }
  336.       /* best? */
  337.       if (0 == try || len < len_best)
  338.         {
  339.           memcpy (pal_best, pal, bpp * psize);
  340.           len_best = len;
  341.         }
  342.     }
  343.       memcpy (pal, pal_best, bpp * psize);
  344.       g_free (pal_best);
  345.       g_free (original);
  346.       /* clean */
  347.       for (i = 1; i < 4 * psize; i++)
  348.     {
  349.       glong as_is, swapd;
  350.       gint i0 = 1 + rand() % (psize - 2);
  351.       gint i1 = i0 + 1;
  352.  
  353.       as_is = (pix_diff (pal, bpp, i0 - 1, i0) +
  354.            pix_diff (pal, bpp, i1, i1 + 1));
  355.       swapd = (pix_diff (pal, bpp, i0 - 1, i1) +
  356.            pix_diff (pal, bpp, i0, i1 + 1));
  357.       if (swapd < as_is)
  358.         {
  359.           pix_swap (pal, bpp, i0, i1);
  360.           len_best += swapd - as_is;
  361.         }
  362.     }
  363.     }
  364.  
  365.   /* store smooth palette */
  366.   gimp_pixel_rgn_init (&pr, new_layer, 0, 0,
  367.                config.width, config.height,
  368.                TRUE, TRUE);
  369.   for (j = 0; j < config.height; j++)
  370.     for (i = 0; i < config.width; i++)
  371.       gimp_pixel_rgn_set_pixel (&pr, pal + bpp * i, i, j);
  372.   g_free (pal);
  373.  
  374.   gimp_drawable_flush (new_layer);
  375.   gimp_drawable_merge_shadow (new_layer->id, TRUE);
  376.   gimp_drawable_update(new_layer->id, 0, 0,
  377.                config.width, config.height);
  378.  
  379.   return new_image_id;
  380. }
  381.  
  382.  
  383. static void
  384. ok_callback (GtkWidget *widget,
  385.          gpointer   data)
  386. {
  387.   run_flag = TRUE;
  388.  
  389.   gtk_widget_destroy (GTK_WIDGET (data));
  390. }
  391.  
  392. static gint
  393. dialog (void)
  394. {
  395.   GtkWidget *dlg;
  396.   GtkWidget *frame;
  397.   GtkWidget *table;
  398.   GtkWidget *spinbutton;
  399.   GtkObject *adj;
  400.  
  401.   gimp_ui_init ("smooth_palette", FALSE);
  402.  
  403.   dlg = gimp_dialog_new (_("Smooth Palette"), "smooth_palette",
  404.              gimp_standard_help_func, "filters/smooth_palette.html",
  405.              GTK_WIN_POS_MOUSE,
  406.              FALSE, TRUE, FALSE,
  407.  
  408.              _("OK"), ok_callback,
  409.              NULL, NULL, NULL, TRUE, FALSE,
  410.              _("Cancel"), gtk_widget_destroy,
  411.              NULL, 1, NULL, FALSE, TRUE,
  412.  
  413.              NULL);
  414.  
  415.   gtk_signal_connect (GTK_OBJECT (dlg), "destroy",
  416.               GTK_SIGNAL_FUNC (gtk_main_quit),
  417.               NULL);
  418.  
  419.   frame = gtk_frame_new (_("Parameter Settings"));
  420.   gtk_container_set_border_width (GTK_CONTAINER (frame), 6);
  421.   gtk_box_pack_start (GTK_BOX (GTK_DIALOG (dlg)->vbox), frame, FALSE, FALSE, 0);
  422.   gtk_widget_show (frame);
  423.  
  424.   table = gtk_table_new (3, 2, FALSE);
  425.   gtk_table_set_row_spacings (GTK_TABLE (table), 2);
  426.   gtk_table_set_col_spacings (GTK_TABLE (table), 4);
  427.   gtk_container_set_border_width (GTK_CONTAINER (table), 4);
  428.   gtk_container_add (GTK_CONTAINER (frame), table);
  429.   gtk_widget_show (table);
  430.  
  431.  
  432.   spinbutton = gimp_spin_button_new (&adj, config.width,
  433.                      1, GIMP_MAX_IMAGE_SIZE, 1, 10, 0, 1, 0);
  434.   gimp_table_attach_aligned (GTK_TABLE (table), 0, 0,
  435.                  _("Width:"), 1.0, 0.5,
  436.                  spinbutton, 1, FALSE);
  437.   gtk_signal_connect (GTK_OBJECT (adj), "value_changed",
  438.               GTK_SIGNAL_FUNC (gimp_int_adjustment_update),
  439.               &config.width);
  440.  
  441.   spinbutton = gimp_spin_button_new (&adj, config.height,
  442.                      1, GIMP_MAX_IMAGE_SIZE, 1, 10, 0, 1, 0);
  443.   gimp_table_attach_aligned (GTK_TABLE (table), 0, 1,
  444.                  _("Height:"), 1.0, 0.5,
  445.                  spinbutton, 1, FALSE);
  446.   gtk_signal_connect (GTK_OBJECT (adj), "value_changed",
  447.               GTK_SIGNAL_FUNC (gimp_int_adjustment_update),
  448.               &config.height);
  449.  
  450.   spinbutton = gimp_spin_button_new (&adj, config.ntries,
  451.                      1, 1024, 1, 10, 0, 1, 0);
  452.   gimp_table_attach_aligned (GTK_TABLE (table), 0, 2,
  453.                  _("Search Time:"), 1.0, 0.5,
  454.                  spinbutton, 1, FALSE);
  455.   gtk_signal_connect (GTK_OBJECT (adj), "value_changed",
  456.               GTK_SIGNAL_FUNC (gimp_int_adjustment_update),
  457.               &config.ntries);
  458.  
  459.   gtk_widget_show (dlg);
  460.  
  461.   gtk_main ();
  462.   gdk_flush ();
  463.  
  464.   return run_flag;
  465. }
  466.